package ru.agiledev.aos.analyzer;

import org.hibernate.Criteria;
import org.hibernate.classic.Session;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.agiledev.akb.ws.AkbAspect;
import ru.agiledev.akb.ws.AkbObject;
import ru.agiledev.aos.commons.dto.LightWord;
import ru.agiledev.aos.commons.dto.QType;
import ru.agiledev.aos.commons.dto.SemanticQuery;
import ru.agiledev.aos.core.util.Config;
import ru.agiledev.aos.core.ws.AKBServiceUtil;
import ru.agiledev.aos.data.HibernateUtil;
import ru.agiledev.aos.data.domain.*;
import ru.agiledev.client.WikiReader;
import ru.agiledev.wikiservice.ConceptObject;
import ru.agiledev.ws.client.AKBServiceClient;

import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

/**
 * Created by a.kirillov
 * Date: 1/16/12 Time: 9:55 PM
 */
public class QueryExtender {
    Logger logger = LoggerFactory.getLogger(Config.class);
    public void extendQuery(SemanticQuery query) {
        QType type = query.getQuestionType();

        switch (type){
            case GOALS_DESCRIPTION: {
                Session session = HibernateUtil.getSessionFactory().openSession();
                session.beginTransaction();
                Criteria criteria;

                if(query.getDistinctiveObject().getValue().contains("успех")){
                    criteria = session.createCriteria(GoalFact.class).add(Restrictions.eq("value", GoalFactValue.POSITIVE));
                } else {
                    criteria = session.createCriteria(GoalFact.class).add(Restrictions.eq("value", GoalFactValue.NEGATIVE));
                }

                List<GoalFact> goals = criteria.list();

                List<GoalFact> properFacts = new ArrayList<GoalFact>();

                //only facts with rules
                for (Iterator<GoalFact> iterator = goals.iterator(); iterator.hasNext();) { //todo: refactor and create proper criterion
                    GoalFact goalFact = iterator.next();
                    if((goalFact.getRules()!=null)&&(goalFact.getRules().size()>0)){
                        properFacts.add(goalFact);
                    }
                }

                if(properFacts.size() == 0) {
                    session.close();
                    return;
                }


                String objectOfInterest = null;
                if((query.getFirstObject()!=null)&&(query.getFirstObject().size()>0)){        //todo: сделать нормализацию слов компания и т.д.
                    StringBuilder lBuilder = new StringBuilder();
                    for (Iterator<LightWord> iterator = query.getFirstObject().iterator(); iterator.hasNext();) {
                        LightWord lightWord = iterator.next();
                        lBuilder.append(lightWord.getValue());
                        lBuilder.append(" ");
                    }
                    objectOfInterest = lBuilder.toString();
                }

                if(objectOfInterest == null){
                    session.close();
                    return;
                }

                String dateObject = "";
                if((query.getDateObject()!=null)&&(query.getDateObject().size()>0)){        //todo: сделать нормализацию слов компания и т.д.
                    StringBuilder lBuilder = new StringBuilder();
                    for (Iterator<LightWord> iterator = query.getDateObject().iterator(); iterator.hasNext();) {
                        LightWord lightWord = iterator.next();
                        lBuilder.append(lightWord.getValue());
                        lBuilder.append(" ");
                    }
                    dateObject = lBuilder.toString();
                }

                List<String> queries = new ArrayList<String>();

                for(GoalFact goalFact: properFacts){
                    List<TransformationRule> transformationRules = goalFact.getRules();

                    for(TransformationRule transformationRule: transformationRules){
                        if(transformationRule.getMorphState().equals(goalFact.getMorphState())){
                            String rule = transformationRule.getRule();
                            rule = rule.replace("#X", objectOfInterest).replace("#A", goalFact.getFact()).replace("#B", goalFact.getAddition()) + " " +dateObject;
                            queries.add(rule);
                        }
                    }
                }

                query.setQueries(queries);

                session.close();
                return;
            }
            case SETS_CHANGES_DESCRIPTION:{
                LightWord first = query.getFirstObject().get(0);

                Session session = HibernateUtil.getSessionFactory().openSession();
                session.beginTransaction();
                Criteria criteria = null;

                boolean twoObjects = false;
                String word1 = null, word2 = null;

                //we should handle situation when we get either one or two OI
                if(query.getSecondObject().size()==0){
                    for(String stem:query.getFirstObject().get(0).getStems()){
                        criteria = session.createCriteria(SetEntity.class).add(Restrictions.eq("value", stem));
                        break;
                    }
                } else {
                    twoObjects = true;
                    for(String stem:query.getFirstObject().get(0).getStems()){
                        word1 = stem;
                        break;
                    }

                    for(String stem:query.getSecondObject().get(0).getStems()){
                        word2 = stem;
                        break;
                    }

                    criteria = session.createCriteria(SetEntity.class).add(Restrictions.or(Restrictions.eq("value", word1), Restrictions.eq("value", word2)));

                }

                List<SetEntity> entities = criteria.list();

                if(entities.size() == 0) return;

                SetEntity detailedEntity = entities.get(0);

                boolean needFirst = true;

                //searching for more deep entity
                if(twoObjects){
                    for(SetEntity entity:entities){
                        if(entity.getDepth() > detailedEntity.getDepth()){
                            detailedEntity = entity;
                        }
                    }

                    if(detailedEntity.getValue().equals(word2)){
                        needFirst = false;
                    } else {
                        needFirst = true;
                    }
                }

                List<String> queries = new ArrayList<String>();

                List<SetComponent> components = detailedEntity.getComponents();
                for(SetComponent component:components){

                    List<LightWord> object = needFirst ? query.getFirstObject() : query.getSecondObject();

                    StringBuilder lBuilder = new StringBuilder();
                    for (Iterator<LightWord> iterator = object.iterator(); iterator.hasNext();) {
                        LightWord lightWord = iterator.next();
                        if(object.indexOf(lightWord)>0){
                            lBuilder.append(" ");
                        }
                        lBuilder.append(lightWord.getValue());
                    }
                    lBuilder.trimToSize();

                    StringBuilder sBuilder = null;
                    if(twoObjects){

                        List<LightWord> commonObject = object.equals(query.getFirstObject()) ? query.getSecondObject() : query.getFirstObject();
                        sBuilder = new StringBuilder();
                        for (Iterator<LightWord> iterator = commonObject.iterator(); iterator.hasNext();) {
                            LightWord lightWord = iterator.next();
                            if(commonObject.indexOf(lightWord)>0){
                                sBuilder.append(" ");
                            }
                            sBuilder.append(lightWord.getValue());
                        }
                    }

                    String date = "";

                    if((query.getDateObject()!=null)&&(query.getDateObject().size()>0)){
                        StringBuilder dateBuilder = new StringBuilder();
                        for (Iterator<LightWord> iterator = query.getDateObject().iterator(); iterator.hasNext();) {
                            LightWord lightWord = iterator.next();
                            if(query.getDateObject().indexOf(lightWord)>0){
                                dateBuilder.append(" ");
                            }
                            dateBuilder.append(lightWord.getValue());
                        }
                        date = dateBuilder.toString();
                    }

                    for(ChangeIndicator indicator: component.getIndicators()){
                        if(twoObjects){
                            queries.add(sBuilder.toString() +" "+ lBuilder.toString()+" "+component.getValue()+" "+indicator.getValue()+" "+date);
                        } else {
                            queries.add(lBuilder.toString()+" "+component.getValue()+" "+indicator.getValue()+" "+date);
                        }
                    }

                }
                query.setQueries(queries);
                return;
            }
            default: {
                AKBServiceClient client = AKBServiceUtil.getClient();
                WikiReader wikiReader = null;
                try {
                    wikiReader = new WikiReader(Config.getInstance().getWikiWSUrl());
                } catch (MalformedURLException e) {
                    logger.error("Некорректно задан URL wiki-сервиса!");
                    return;
                }

                if(query.getDistinctiveObject() == null){
                    switch (type){
                        case STRUCTURE_DESCRIPTION: query.setDistinctiveObject(new LightWord("структура", new HashSet<String>())); break;
                        case CHARACTERISTICS_DESCRIPTION: query.setDistinctiveObject(new LightWord("характеристика", new HashSet<String>())); break;
                        case WORK_DESCRIPTION: query.setDistinctiveObject(new LightWord("работа", new HashSet<String>())); break;
                        case FUNCTIONS_DESCRIPTION: query.setDistinctiveObject(new LightWord("функция", new HashSet<String>())); break;
                        case MISSION_DESCRIPTION: query.setDistinctiveObject(new LightWord("назначение", new HashSet<String>())); break;
                        case USAGE_DESCRIPTION: query.setDistinctiveObject(new LightWord("применение", new HashSet<String>())); break;
                        case CORRESPONDS_TO_DESCRIPTION: query.setDistinctiveObject(new LightWord("принадлежность", new HashSet<String>())); break;
                        case DIFFERENCES_DESCRIPTION: query.setDistinctiveObject(new LightWord("различие", new HashSet<String>())); break;
                        case COMMONS_DESCRIPTION: query.setDistinctiveObject(new LightWord("общий", new HashSet<String>())); break;
                        case FEATURES_DESCRIPTION: query.setDistinctiveObject(new LightWord("особенность", new HashSet<String>())); break;
                        case POSSIBILITIES_DESCRIPTION: query.setDistinctiveObject(new LightWord("возможности", new HashSet<String>())); break;
                    }
                }

                String dObject = null;
                if(query.getDistinctiveObject().getStems().size()>0){
                    dObject = query.getDistinctiveObject().getStems().iterator().next();
                } else {
                    dObject = query.getDistinctiveObject().getValue();
                }

                ConceptObject env = wikiReader.getConceptualEnvironment(dObject);

                AkbObject akbObject = null;

                try {
                    akbObject = client.getObjectWithAspects(query.getFirstObject().get(0).getStems().iterator().next(), transformType(query.getQuestionType()));
                } catch (Exception e) {
                    logger.warn("Ошибка обращения к веб-сервису АОБЗ: "+e.getMessage());
                }

                String objectOfInterest = "";
                if((query.getFirstObject()!=null)&&(query.getFirstObject().size()>0)){
                    StringBuilder lBuilder = new StringBuilder();
                    for (Iterator<LightWord> iterator = query.getFirstObject().iterator(); iterator.hasNext();) {
                        LightWord lightWord = iterator.next();
                        lBuilder.append(lightWord.getValue());
                        lBuilder.append(" ");
                    }

                    if ((query.getSecondObject()!=null)&&(query.getSecondObject().size()>0)){
                        lBuilder.append(" ");
                        for (Iterator<LightWord> iterator = query.getSecondObject().iterator(); iterator.hasNext();) {
                            LightWord lightWord = iterator.next();
                            lBuilder.append(lightWord.getValue());
                            lBuilder.append(" ");
                        }
                    }

                    objectOfInterest = lBuilder.toString();
                }

                String additionalObject = "";

                if((query.getAdditionalObject()!=null)&&(query.getAdditionalObject().size()>0)){
                    StringBuilder lBuilder = new StringBuilder();
                    for (Iterator<LightWord> iterator = query.getAdditionalObject().iterator(); iterator.hasNext();) {
                        LightWord lightWord = iterator.next();
                        lBuilder.append(lightWord.getValue());
                        lBuilder.append(" ");
                    }

                    objectOfInterest = lBuilder.toString();
                }

                List<String> queries = new ArrayList<String>();

                if((akbObject == null)&&(env!=null)){
                    StringBuilder builder = new StringBuilder();

                    builder.append(dObject);
                    builder.append(" ");
                    builder.append(objectOfInterest);
                    builder.append(additionalObject);

                    queries.add(builder.toString());

                    for(String word: env.getSynonyms()){
                        if(word.length()>2){
                            if(word.contains("(") ||word.contains(")") || word.contains(":")) continue;
                            builder = new StringBuilder();
                            builder.append(word);
                            builder.append(" ");
                            builder.append(objectOfInterest);
                            builder.append(additionalObject);

                            queries.add(builder.toString());
                        }
                    }

                    for(String word: env.getHyponyms()){
                        if(word.length()>2){
                            if(word.contains("(") ||word.contains(")") || word.contains(":")) continue;
                            builder = new StringBuilder();
                            builder.append(word);
                            builder.append(" ");
                            builder.append(objectOfInterest);
                            builder.append(additionalObject);

                            queries.add(builder.toString());
                        }
                    }

                    for(String word: env.getHypernyms()){
                        if(word.length()>2){
                            if(word.contains("(") ||word.contains(")") || word.contains(":")) continue;
                            builder = new StringBuilder();
                            builder.append(word);
                            builder.append(" ");
                            builder.append(objectOfInterest);
                            builder.append(additionalObject);

                            queries.add(builder.toString());
                        }
                    }
                } else if ((akbObject != null)&&(env == null)) {
                     for(AkbAspect aspect: akbObject.getAspects()){
                        StringBuilder builder = new StringBuilder();

                        builder.append(dObject);
                        builder.append(" ");
                        builder.append(objectOfInterest);
                        builder.append(aspect.getValue());
                        builder.append(" ");
                        builder.append(additionalObject);

                        queries.add(builder.toString());
                     }
                }  else if ((akbObject != null)&&(env != null)){

                    for(AkbAspect aspect: akbObject.getAspects()){
                        StringBuilder builder = new StringBuilder();

                        builder.append(dObject);
                        builder.append(" ");
                        builder.append(objectOfInterest);
                        builder.append(aspect.getValue());
                        builder.append(" ");
                        builder.append(additionalObject);

                        queries.add(builder.toString());

                        for(String word: env.getSynonyms()){
                            if(word.length()>2){
                                if(word.contains("(") ||word.contains(")") || word.contains(":")) continue;
                                builder = new StringBuilder();

                                builder.append(word);
                                builder.append(" ");
                                builder.append(objectOfInterest);
                                builder.append(aspect.getValue());
                                builder.append(" ");
                                builder.append(additionalObject);

                                queries.add(builder.toString());

                            }
                        }

                        for(String word: env.getHyponyms()){
                            if(word.length()>2){
                                if(word.contains("(") ||word.contains(")") || word.contains(":")) continue;
                                builder = new StringBuilder();
                                builder.append(word);
                                builder.append(" ");
                                builder.append(objectOfInterest);
                                builder.append(aspect.getValue());
                                builder.append(" ");
                                builder.append(additionalObject);

                                queries.add(builder.toString());
                            }
                        }

                        for(String word: env.getHypernyms()){
                            if(word.length()>2){
                                if(word.contains("(") ||word.contains(")") || word.contains(":")) continue;
                                builder = new StringBuilder();
                                builder.append(word);
                                builder.append(" ");
                                builder.append(objectOfInterest);
                                builder.append(aspect.getValue());
                                builder.append(" ");
                                builder.append(additionalObject);

                                queries.add(builder.toString());
                            }
                        }

                    }
                } else return;
                //remove duplicates
                HashSet h = new HashSet(queries);
                queries.clear();
                queries.addAll(h);

                query.setQueries(queries);

            }
        }
        printQueries(query.getQueries());
    }

    /**
     * Helper method
     *todo: remove in future version
     * @param queries list of generated queries
     */
    private void printQueries(List<String> queries){
        for(String query:queries){
            System.out.println(query);
        }
    }

    private ru.agiledev.akb.ws.QType transformType(QType type){
        switch (type){
            case STRUCTURE_DESCRIPTION: return ru.agiledev.akb.ws.QType.STRUCTURE_DESCRIPTION;
            case CHARACTERISTICS_DESCRIPTION: return ru.agiledev.akb.ws.QType.CHARS_DESCRIPTION;
            case WORK_DESCRIPTION: return ru.agiledev.akb.ws.QType.WORK_DESCRIPTION;
            case FUNCTIONS_DESCRIPTION: return ru.agiledev.akb.ws.QType.FUNCTIONS_DESCRIPTION;
            case MISSION_DESCRIPTION: return ru.agiledev.akb.ws.QType.PURPOSE_DESCRIPTION;
            case USAGE_DESCRIPTION: return ru.agiledev.akb.ws.QType.USAGE_DESCRIPTION;
            case CORRESPONDS_TO_DESCRIPTION: return ru.agiledev.akb.ws.QType.BELONGING_DESCRIPTION;
            case DIFFERENCES_DESCRIPTION: return ru.agiledev.akb.ws.QType.DIFFERENCES_DESCRIPTION;
            case COMMONS_DESCRIPTION: return ru.agiledev.akb.ws.QType.COMMONS_DESCRIPTION;
            case FEATURES_DESCRIPTION: return ru.agiledev.akb.ws.QType.FEATURES_DESCRIPTION;
            case POSSIBILITIES_DESCRIPTION: return ru.agiledev.akb.ws.QType.CAPABILITIES_DESCRIPTION;
        }
        return null;
    }
}
